package com.drakulo.games.ais.core.io;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import com.drakulo.games.ais.core.Resource;
import com.drakulo.games.ais.core.Settings;
import com.drakulo.games.ais.core.tech.Technology;
import com.drakulo.games.ais.ui.I18n;

public class TechnologyIO {

	public static Map<String, Technology> loadTechnologies() {
		Map<String, Technology> map = null;

		File f = new File("./data/technologies.xml");
		SAXBuilder sxb = new SAXBuilder();
		try {
			Document document = sxb.build(f);
			Element root = document.getRootElement();
			map = handleNodes(root);
			// The map is loaded. Each technology will now be linked to it's
			// parents and children
			Set<String> keys = map.keySet();
			for (String key : keys) {
				List<Technology> parents = new ArrayList<Technology>();
				List<Technology> children = new ArrayList<Technology>();
				Technology t = map.get(key);
				for (Technology pt : t.getParents()) {
					parents.add(map.get(pt.getId()));
				}
				for (Technology ct : t.getChildren()) {
					children.add(map.get(ct.getId()));
				}
				t.setParents(parents);
				t.setChildren(children);
			}

			calculateLevels(map);

			loadCosts(root, map);

			return map;
		} catch (JDOMException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return null;
	}

	/**
	 * Analyse the costs from the XML stream and loads it into each technology
	 * 
	 * @param root
	 *            - the root node
	 * @param map
	 *            - the technology map
	 */
	@SuppressWarnings("unchecked")
	private static void loadCosts(Element root, Map<String, Technology> map) {
		Element costsElement = root.getChild("costs");
		List<Element> costsByLevel = costsElement.getChildren("level");
		Map<String, Map<Resource, BigDecimal>> costMap = new HashMap<String, Map<Resource, BigDecimal>>();
		for (Element costElement : costsByLevel) {
			String level = costElement.getAttribute("value").getValue();
			Resource[] resources = Resource.values();
			Map<Resource, BigDecimal> temp = new HashMap<Resource, BigDecimal>();
			for (Resource r : resources) {
				Element e = costElement.getChild(r.toString().toLowerCase());
				if (e == null) {
					temp.put(r, BigDecimal.ZERO);
				} else {
					temp.put(r, BigDecimal.valueOf(Long.valueOf(e.getText())));
				}
			}
			costMap.put(level, temp);
		}

		for (String k : map.keySet()) {
			Technology t = map.get(k);
			int level = t.getLevel();
			t.setCost(costMap.get(String.valueOf(level)));
		}

	}

	/**
	 * Determine the technology level according to its parents / children
	 * 
	 * @param map
	 *            - the data map
	 */
	private static void calculateLevels(Map<String, Technology> map) {
		for (String k : map.keySet()) {
			initLevel(map.get(k));
		}
	}

	private static void initLevel(Technology t) {
		if (t.getLevel() != -1) {
			// The level is already initialized
			return;
		}

		List<Technology> parents = t.getParents();
		// No parents => root technology => level 0;
		if (parents.size() == 0) {
			t.setLevel(0);
			return;
		}

		int maxParentLevel = 0;
		for (Technology parent : parents) {
			if (parent.getLevel() == -1) {
				// The parent is not initialized yet
				initLevel(parent);
			}
			if (maxParentLevel < parent.getLevel()) {
				maxParentLevel = parent.getLevel();
			}
		}
		t.setLevel(maxParentLevel + 1);
	}

	@SuppressWarnings("unchecked")
	private static Map<String, Technology> handleNodes(Element root) {
		Map<String, Technology> map = new HashMap<String, Technology>();

		List<Element> technologies = root.getChildren("technology");
		// System.out.println("===================================");
		// System.out.println("===================================");
		for (Element te : technologies) {
			Technology t = createFromNode(te);
			// System.out.println(t.getId()+".name = ");
			// System.out.println(t.getId()+".description = ");
			// System.out.println("");
			map.put(t.getId(), t);
		}
		// System.out.println("===================================");
		// System.out.println("===================================");

		return map;
	}

	@SuppressWarnings("unchecked")
	private static Technology createFromNode(Element node) {
		Technology t = new Technology();
		String id = node.getChildText("id");
		t.setId(id);
		t.setName(I18n.getFirstToUpper(id + ".name"));
		t.setDescription(I18n.getFirstToUpper(id + ".description"));
		t.setImageKey(node.getChildText("icon"));

		List<Element> parents = node.getChild("parents").getChildren("parent");
		for (Element p : parents) {
			Technology pt = new Technology();
			pt.setId(p.getText());
			t.addParent(pt);
		}

		List<Element> children = node.getChild("children").getChildren("child");
		for (Element p : children) {
			Technology ct = new Technology();
			ct.setId(p.getText());
			t.addChild(ct);
		}

		// Debug mode
		if (Settings.IS_DEBUG) {
			t.setOwned(true);
		}
		return t;
	}
}
